/**
 *******************************************************************************
 * @file    system_TMPM470.c
 * @brief   CMSIS Cortex-M4 Device Peripheral Access Layer Source File for the
 *          TOSHIBA 'TMPM470' Device Series 
 * @version V2.0.2.1
 * @date    2014/12/26
 * 
 * DO NOT USE THIS SOFTWARE WITHOUT THE SOFTWARE LISENCE AGREEMENT.
 * 
 * (C)Copyright TOSHIBA CORPORATION 2014 All rights reserved
 *******************************************************************************
 */
 
#include "../../4.Driver/Include/WROS_Register.h"

#include "../../5.Hardware/Include/bootloader.h"

/*-------- <<< Start of configuration section >>> ----------------------------*/

/* Watchdog Timer (WD) Configuration */
#define WD_SETUP                (1U)
#define WDMOD_Val               (0x00000000UL)
#define WDCR_Val                (0x000000B1UL)

/* Clock Generator (CG) Configuration */
//#define INTERNAL_OSCILLATION
#ifdef INTERNAL_OSCILLATION
#define CLOCK_SETUP             0U         /* Use the internal oscilation */
#else
#define CLOCK_SETUP             1U         /* Use the external oscilation */
#endif

#define OSCCR_Val               (0x000A0104UL) /* OSCCR<OSCSEL> = 1, OSCCR<XEN2> = 0, OSCCR<XEN1> = 1, OSCCR<PLLON> = 1, <WUPSEL2> = 1*/
#define STBYCR_Val              (0x00000103UL)

#define CG_SYSCR_GEAR_MASK      (0xFFFFFFF8UL)
#define CG_SYSCR_GEAR_0         (0x00000000UL)
#define CG_SYSCR_GEAR_2         (0x00000004UL)
#define CG_SYSCR_GEAR_4         (0x00000005UL)
#define CG_SYSCR_GEAR_8         (0x00000006UL)
#define CG_SYSCR_GEAR_16        (0x00000007UL)
#define CG_SYSCR_GEAR_SET        CG_SYSCR_GEAR_0
#define SYSCR_Val               (0x00010100UL)

#define CG_8M_MUL_4_FPLL        (0x0000591EUL<<1U)	/* PLL 10 * 8 */
#define CG_10M_MUL_4_FPLL       (0x00005926UL<<1U)	/* PLL 10 * 10 */
#define CG_12M_MUL_4_FPLL       (0x000059AEUL<<1U)	/* PLL 10 * 12 */
#define PLLSEL_MASK             (0xFFFF0001UL)
#define PLLSEL_Ready             CG_12M_MUL_4_FPLL
#define PLLSEL_Val              (PLLSEL_Ready | 1U)

/*-------- <<< End of configuration section >>> ------------------------------*/

/*-------- DEFINES -----------------------------------------------------------*/
/* Define clocks */
#define OSC_8M                  ( 8000000UL)
#define OSC_10M                 (10000000UL)
#define EXTALH                  OSC_10M     /* External high-speed oscillator freq */
#define XTALH                   OSC_10M     /* Internal high-speed oscillator freq */

/* Configure Warm-up time */
#define HZ_1M                  (1000000UL)
#define WU_TIME_EXT            (5000UL)          /* warm-up time for EXT is 5ms   */
#define WU_TIME_PLL_100        (100UL)           /* warm-up time for PLL is 100us */
#define WU_TIME_PLL_200        (200UL)           /* warm-up time for PLL is 200us */
#define OSCCR_WUODR_MASK       (0x000FFFFFUL)
#define OSCCR_WUODR_EXT        ((WU_TIME_EXT * EXTALH / HZ_1M / 16UL) << 20U)     /* OSCCR<WUPODR[11:0]> = warm-up time(us) * EXTALH(MHz) / 16 */
#define OSCCR_WUODR_PLL_100    ((WU_TIME_PLL_100 * EXTALH / HZ_1M / 16UL) << 20U)
#define OSCCR_WUODR_PLL_200    ((WU_TIME_PLL_200 * EXTALH / HZ_1M / 16UL) << 20U)

#if (CLOCK_SETUP)               /* Clock(external) Setup */
/* Determine core clock frequency according to settings */
/* System clock is high-speed clock*/
#if (OSCCR_Val & (1U<<17))
  #define CORE_TALH (EXTALH)
#else
  #define CORE_TALH (XTALH)
#endif

#if ((PLLSEL_Val & (1U<<0)) && (OSCCR_Val & (1U<<2))) /* If PLL selected and enabled */
  #if   ((SYSCR_Val & 7U) == 0U)      /* Gear -> fc                         */
    #define __CORE_SYS   (CORE_TALH * 4U )
  #elif ((SYSCR_Val & 7U) == 4U)      /* Gear -> fc/2                       */
    #define __CORE_SYS   (CORE_TALH * 4U / 2U)
  #elif ((SYSCR_Val & 7U) == 5U)      /* Gear -> fc/4                       */
    #define __CORE_SYS   (CORE_TALH * 4U / 4U )
  #elif ((SYSCR_Val & 7U) == 6U)      /* Gear -> fc/8                       */
    #define __CORE_SYS   (CORE_TALH * 4U / 8U)
  #elif ((SYSCR_Val & 7U) == 7U)      /* Gear -> fc/16                      */
    #define __CORE_SYS   (CORE_TALH * 4U / 16U)
  #else                               /* Gear -> reserved                   */
    #define __CORE_SYS   (0U)
#endif
#else                                 /* If PLL not used                    */
  #if   ((SYSCR_Val & 7U) == 0U)      /* Gear -> fc                         */
    #define __CORE_SYS   (CORE_TALH)
  #elif ((SYSCR_Val & 7U) == 4U)      /* Gear -> fc/2                       */
    #define __CORE_SYS   (CORE_TALH / 2U)
  #elif ((SYSCR_Val & 7U) == 5U)      /* Gear -> fc/4                       */
    #define __CORE_SYS   (CORE_TALH / 4U)
  #elif ((SYSCR_Val & 7U) == 6U)      /* Gear -> fc/8                       */
    #define __CORE_SYS   (CORE_TALH / 8U)
  #elif ((SYSCR_Val & 7U) == 7U)      /* Gear -> fc/16                       */
    #define __CORE_SYS   (CORE_TALH / 16U)
  #else                               /* Gear -> reserved                   */
    #define __CORE_SYS   (0U)
  #endif
#endif

#else
  #define __CORE_SYS   (XTALH)
  
#endif                                                  /* clock Setup */

/* Clock Variable definitions */
uint32_t SystemCoreClock = __CORE_SYS;  /*!< System Clock Frequency (Core Clock) */

/**
 * Initialize the system
 *
 * @param  none
 * @return none
 *
 * @brief  Update SystemCoreClock according register values.
 */
struct OSCCR_Register	OSCCR_registers;
struct PLLSEL_Register	PLLSEL_registers;

void SystemCoreClockUpdate(void)
{
    /* Get Core Clock Frequency */
    uint32_t CoreClock = 0U;
    /* Determine clock frequency according to clock register values */
    /* System clock is high-speed clock */
    if (WROS_CLK_GE->OSCCR_register.OSCSEL) {  /* If system clock is External high-speed oscillator freq */
        CoreClock = EXTALH;
    } else {                    /* If system clock is Internal high-speed oscillator freq */
        CoreClock = XTALH;
    }
    if (WROS_CLK_GE->PLLSEL_register.PLLSEL && WROS_CLK_GE->OSCCR_register.PLLON) {   /* If PLL selected and enabled */
       switch (WROS_CLK_GE->SYSCR_register.PRCK) {
       case 0U:                                 /* Gear -> fc          */
         SystemCoreClock = CoreClock * 4U ;
         break;
       case 1U:
       case 2U:
       case 3U:                                 /* Gear -> reserved   */
         SystemCoreClock = 0U;
         break;
       case 4U:                                 /* Gear -> fc/2      */
         SystemCoreClock = CoreClock * 4U / 2U;
         break;
       case 5U:                                 /* Gear -> fc/4      */
         SystemCoreClock = CoreClock * 4U / 4U;
         break;
       case 6U:                                 /* Gear -> fc/8      */
         SystemCoreClock = CoreClock * 4U / 8U;
         break;
       case 7U:                                 /* Gear -> fc/16      */
         SystemCoreClock = CoreClock * 4U / 16U;
         break;
       default:
         SystemCoreClock = 0U;
       }
    } else {                                     /* If PLL not used      */
       switch (WROS_CLK_GE->SYSCR_register.PRCK) {
       case 0U:                                 /* Gear -> fc          */
         SystemCoreClock = CoreClock;
         break;
       case 1U:
       case 2U:
       case 3U:
       case 7U:                                 /* Gear -> reserved    */
         SystemCoreClock = 0U;
         break;
       case 4U:                                 /* Gear -> fc/2        */
         SystemCoreClock = CoreClock / 2U;
         break;
       case 5U:                                 /* Gear -> fc/4        */
         SystemCoreClock = CoreClock / 4U;
         break;
       case 6U:                                 /* Gear -> fc/8        */
         SystemCoreClock = CoreClock / 8U;
         break;
       default:
         SystemCoreClock = 0U;
       }
    }
}

/**
 * Initialize the system
 *
 * @param  none
 * @return none
 *
 * @brief  Setup the microcontroller system.
 *         Initialize the System.
 */
void SystemInit(void)
{
#if (WD_SETUP)                  /* Watchdog Setup */
	WROS_WDT__0->WDT_MOD = CLEAR;
	if (!(WROS_WDT__0->WDT_MOD_register.WDTE)) {     /* If watchdog is to be disabled */
	WROS_WDT__0->WDT_CR = WDCR_Val;
	} 
	else {
	}
#endif
	// TODO : OFD off code 甸绢啊具 窃!
#if (CLOCK_SETUP)               /* Clock(external) Setup */
	/* Switch over from the internal oscillator to the external oscillator. */


	WROS_CLK_GE->OSCCR &= OSCCR_WUODR_MASK;
	WROS_CLK_GE->OSCCR |= OSCCR_WUODR_EXT;

	OSCCR_registers = WROS_CLK_GE->OSCCR_register;
	OSCCR_registers.XEN1	= SET;
	OSCCR_registers.WUPSEL2	= SET;
	OSCCR_registers.WUEON	= SET;
	WROS_CLK_GE->OSCCR_register = OSCCR_registers;



	while (WROS_CLK_GE->OSCCR_register.WUEF) {
	}                          

	OSCCR_registers = WROS_CLK_GE->OSCCR_register;
	OSCCR_registers.OSCSEL	= SET;
	WROS_CLK_GE->OSCCR_register = OSCCR_registers;

	while (WROS_CLK_GE->OSCCR_register.OSCSEL != 1U) {
	}                          
	OSCCR_registers = WROS_CLK_GE->OSCCR_register;
	OSCCR_registers.XEN2	= CLEAR;
	WROS_CLK_GE->OSCCR_register = OSCCR_registers;

#endif

	/* Set PLL and select PLL as fc source */
	WROS_CLK_GE->OSCCR &= OSCCR_WUODR_MASK;
	WROS_CLK_GE->OSCCR |= OSCCR_WUODR_PLL_100;
	WROS_CLK_GE->PLLSEL &= PLLSEL_MASK;
	WROS_CLK_GE->PLLSEL |= PLLSEL_Ready;

	OSCCR_registers = WROS_CLK_GE->OSCCR_register;
	OSCCR_registers.WUEON	= SET;
	WROS_CLK_GE->OSCCR_register = OSCCR_registers;

	while (WROS_CLK_GE->OSCCR_register.WUEF) {
	} 
	WROS_CLK_GE->OSCCR &= OSCCR_WUODR_MASK;
	WROS_CLK_GE->OSCCR |= OSCCR_WUODR_PLL_200;

	OSCCR_registers = WROS_CLK_GE->OSCCR_register;
	OSCCR_registers.PLLON	= SET;   
	WROS_CLK_GE->OSCCR_register = OSCCR_registers;

	OSCCR_registers = WROS_CLK_GE->OSCCR_register;
	OSCCR_registers.WUEON	= SET;
	WROS_CLK_GE->OSCCR_register = OSCCR_registers;

	while (WROS_CLK_GE->OSCCR_register.WUEF) {
	}                          

	PLLSEL_registers = WROS_CLK_GE->PLLSEL_register;
	PLLSEL_registers.PLLSEL	= SET;
	WROS_CLK_GE->PLLSEL_register = PLLSEL_registers;


	WROS_CLK_GE->SYSCR = SYSCR_Val;
	WROS_CLK_GE->SYSCR &= CG_SYSCR_GEAR_MASK;
	WROS_CLK_GE->SYSCR |= CG_SYSCR_GEAR_SET;

	WROS_CLK_GE->STBYCR = STBYCR_Val;
}
